package server

import (
	"log"
	"net"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"

	sadefine "gitee.com/simplexyz/simplego/actor/define"
	sawork "gitee.com/simplexyz/simplego/actor/work"
	slog "gitee.com/simplexyz/simplego/log"
)

type requestHandler struct {
	*HandlerOption
	funcs            []HandlerFunc
	lastDispatchTime time.Time
}

type Server struct {
	listener    net.Listener
	mux         *gin.Engine
	server      *http.Server
	handlers    map[string]*requestHandler
	rootContext *sadefine.RootContext
	pid         *sadefine.PID
	logger      slog.ILogger
}

func New(rootContext *sadefine.RootContext, pid *sadefine.PID, logger slog.ILogger) *Server {
	serverMux := gin.New()
	serverMux.Use(gin.Recovery())

	s := &Server{
		mux:         serverMux,
		handlers:    map[string]*requestHandler{},
		rootContext: rootContext,
		pid:         pid,
		logger:      logger,
	}

	s.server = &http.Server{
		Handler:           s.mux,
		ReadTimeout:       5 * time.Second,
		ReadHeaderTimeout: 3 * time.Second,
		WriteTimeout:      10 * time.Second,
		IdleTimeout:       120 * time.Second,
	}

	return s
}

func (s *Server) Listener() net.Listener {
	return s.listener
}

func (s *Server) ListenAndServe(addr string) (listenAddr string, err error) {
	s.listener, err = net.Listen("tcp", addr)
	if err != nil {
		return
	}

	listenAddr = s.listener.Addr().String()

	go func() {
		err := s.server.Serve(s.listener)
		if err != nil {
			s.logger.Errorf("http server Serve fail, %s", err)
			return
		}
	}()

	return
}

func (s *Server) ListenAndServeTLS(addr string, certFile string, keyFile string) (listenAddr string, err error) {
	s.listener, err = net.Listen("tcp", addr)
	if err != nil {
		return
	}

	listenAddr = s.listener.Addr().String()

	go func() {
		err := s.server.ServeTLS(s.listener, certFile, keyFile)
		if err != nil {
			s.logger.Errorf("https server ServeTLS fail, %s", err)
			return
		}
	}()

	return
}

func (s *Server) MustRegisterHandler(method string, pattern string, option *HandlerOption, funcs ...HandlerFunc) {
	if _, ok := s.handlers[pattern]; ok {
		log.Panicf("http request handler already register, pattern=%s", pattern)
	}

	if option == nil {
		option = NewHandlerOption()
	}

	for _, f := range funcs {
		if f == nil {
			log.Panicf("http request handler func must not nil, pattern=%s", pattern)
		}
	}

	var h = &requestHandler{
		funcs:         funcs,
		HandlerOption: option,
	}

	s.handlers[pattern] = h

	if option.queued {
		funcs = append([]HandlerFunc{
			func(ctx *Context) {
				//TraceRequestBody(s.logger)(ctx)

				err := sawork.Dispatch(s.rootContext, s.pid, h.timeout, sawork.DispatchFunc(func(actx sadefine.Context) error {
					now := time.Now()

					if h.minDispatchInterval > 0 {
						if now.Sub(h.lastDispatchTime) < h.minDispatchInterval {
							s.logger.Debugf("[%s] request too often", ctx.Request.URL.Path)
							ctx.Abort()
							return ErrTooOften
						}
					}

					h.lastDispatchTime = now

					ctx.Next()

					return nil
				}))
				if err != nil {
					s.logger.Errorf("[%s] dispatch fail, %v", ctx.Request.URL.Path, err)
					return
				}
			},
		}, funcs...)
	}

	s.mux.Handle(method, pattern, funcs...)

	return
}
